let count = 0;

/**
 * 标准化自定义元素名称，将名称从 CamelCase 转换为 kebab-case
 * @param name 自定义元素名称
 * @returns 标准化后的元素名称
 */
export function normalizeElementName(name: string): string {
    return name.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()).replace(/^-/, "") + "-" + ++count;
}

/**
 * 注册自定义元素
 *
 * @param constructor
 * @param extend      继承的标签名
 */
export function define(constructor: CustomElementConstructor, extend?: keyof HTMLElementTagNameMap | undefined): void;
/**
 * 注册自定义元素
 * @deprecated
 *
 * @param name        自定义元素名称
 * @param constructor 构造函数
 * @param options     可选参数
 */
export function define(name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions): void;
export function define(
    nameOrCtor: string | CustomElementConstructor,
    constructor?: CustomElementConstructor | keyof HTMLElementTagNameMap | undefined,
    options?: ElementDefinitionOptions
): void {
    if (typeof nameOrCtor === "string") {
        customElements.define(nameOrCtor, constructor as CustomElementConstructor, options);
    } else {
        const ctor = nameOrCtor;
        const name = normalizeElementName(ctor.name);
        console.log("register", name);
        if (typeof constructor === "string") {
            customElements.define(name, ctor, { extends: constructor });
        } else {
            customElements.define(name, ctor);
        }
    }
}

export type HTMLElementTagName = keyof HTMLElementTagNameMap;

interface ElementConstructor<T extends HTMLElement = HTMLElement> {
    new (): T;
}

type ElementFactory<T extends HTMLElement = HTMLElement> = HTMLElementTagName | ElementConstructor<T>;

type ElementOf<T> = T extends ElementConstructor<infer E>
    ? E
    : T extends HTMLElementTagName
    ? HTMLElementTagNameMap[T]
    : never;

/**
 * 根据标签名或构造函数创建元素
 * @param map name 与标签名或构造函数的映射
 * @returns NameElementMap<T>
 */
export function createElements<T extends Record<string, ElementFactory<HTMLElement>>>(
    map: T
): { [k in keyof T]: ElementOf<T[k]> } {
    const result = Object.create(null);
    for (const key in map) {
        const fty = map[key as keyof T];
        if (fty instanceof Function) {
            result[key] = new fty() as any;
            continue;
        }
        result[key] = document.createElement(fty as string);
    }
    return result;
}

export interface CustomElement {
    connectedCallback?(): void;
    disconnectedCallback?(): void;
    adoptedCallback?(): void;
    attributeChangedCallback?(name: string, oldValue: string, newValue: string): void;
}
